package com.bootdo.app.common.utils;

import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @auther: 作者: Along
 * @description: RSA加密算法
 * RSA是一种非对称加密算法。
 * 一般需要生成公私钥，
 * 使用公钥加密，需要使用私钥解密
 * 使用私钥加密，需要使用公钥解密
 */
public class RSAKeyCreator {

// ---------------公私钥生成---------------
    /**
     * 生成公私钥文件
     * @param publicKeyFilename 公钥文件
     * @param privateKeyFilename 私钥文件
     * @param password 加密内容
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     * @throws IOException
     */
    public static void createPubKeyAndPriKeyToFile(String publicKeyFilename,String privateKeyFilename,String password) throws NoSuchAlgorithmException, IOException {
        // KeyPairGenerator 类用于生成公钥和私钥对。密钥对生成器是使用 getInstance 工厂方法（返回一个给定类的实例的静态方法）构造的。
        // rsa 生成密钥4步骤：1. 通过工厂方法创建对象 2. 初始化该对象 3. 生成密钥对 4.通过密钥对对象获取公私钥
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        // 2. 初始化KeyPairGenerator
        keyPairGenerator.initialize(1024, new SecureRandom(password.getBytes()));
        // 3. 生成密钥对
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        FileOutputStream fos = new FileOutputStream(publicKeyFilename);
        fos.write(publicKeyBytes);
        fos.close();
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        fos = new FileOutputStream(privateKeyFilename);
        fos.write(privateKeyBytes);
        fos.close();
    }

    /**
     * 生成公私钥
     * @param password 密钥
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static Map<String,byte[]> createPubKeyAndPriKey(String password) throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024,new SecureRandom(password.getBytes()));
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] pub = keyPair.getPublic().getEncoded();
        byte[] pri = keyPair.getPrivate().getEncoded();
        Map<String,byte[]> map = new HashMap<>();
        map.put("pub",pub);
        map.put("pri",pri);
        return map;
    }

    /**
     * 公钥生成
     * @param password 加密内容
     * @return
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     */
    public static byte[] createPubKey(String password) throws NoSuchAlgorithmException {
        // 1. 通过KeyPairGenerator的工厂方法创建密钥对生成对象KeyPairGenerator
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        // 2. 初始化KeyPairGenerator
        keyPairGenerator.initialize(1024, new SecureRandom(password.getBytes()));
        // 3. 生成密钥对
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 4. 获取公钥
        return keyPair.getPublic().getEncoded();
    }

    /**
     * 私钥生成
     * @param password 加密内容
     * @return
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     */
    public static byte[] createPriKey(String password) throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024,new SecureRandom(password.getBytes()));
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        return keyPair.getPrivate().getEncoded();
    }

// ---------------获取公钥---------------
    /**
     * 从系统文件中获取公钥
     * @param fileName 系统公钥文件路径
     * @return PublicKey
     * @throws IOException
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     * @throws InvalidKeySpecException 无效密钥规范异常
     */
    public PublicKey getPubKeyFromFile(String fileName) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        DataInputStream dataInputStream = new DataInputStream(this.getClass().getClassLoader().getResourceAsStream(fileName));
        byte[] encodedKey = new byte[dataInputStream.available()];
        dataInputStream.readFully(encodedKey);
        dataInputStream.close();
        // X509EncodedKeySpec 此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码。
        return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedKey));

    }

    /**
     * 获取公钥
     * @param encodedKey 公钥字节数据
     * @return
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     * @throws InvalidKeySpecException 无效密钥规范异常
     */
    public PublicKey getPubKeyFromByte(byte[] encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedKey));
    }

// ---------------获取私钥---------------
    /**
     * 从系统中文件中获取私钥
     * @param fileName 系统私钥文件路径
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     * @throws InvalidKeySpecException 无效密钥规范异常
     */
    public PrivateKey getPriKeyFromFile(String fileName) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        DataInputStream dataInputStream = new DataInputStream(this.getClass().getClassLoader().getResourceAsStream(fileName));
        byte[] encodedKey = new byte[dataInputStream.available()];
        dataInputStream.readFully(encodedKey);
        dataInputStream.close();
        //PKCS8EncodedKeySpec 此类表示按照 ASN.1 类型 PrivateKeyInfo 进行编码的专用密钥的 ASN.1 编码。
        return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    /**
     * 获取私钥
     * @param encodedKey 私钥字节数据
     * @return
     * @throws NoSuchAlgorithmException 加密算法不可用异常
     * @throws InvalidKeySpecException 无效密钥规范异常
     */
    public PrivateKey getPriKeyFromByte(byte[] encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }


// ---------------Base64---------------
    public static String base64Encode(byte[] bys){
        return Base64.getEncoder().encodeToString(bys);

    }
    public static byte[] base64Decode(String str) throws IOException {
        return Base64.getDecoder().decode(str);
    }



    public static void main(String[] args) throws Exception {
//        String password = "123456";
//        byte[] pubKey = createPubKey(password);
//        byte[] priKey = createPriKey(password);
//        String pubKey_encode = base64Encode(pubKey);
//        System.out.println("pubKey_encode = " + pubKey_encode);
//        String priKey_encode = base64Encode(priKey);
//        System.out.println("priKey_encode = " + priKey_encode);

    }

}
